using System;
using System.Windows.Input;
using Avalonia.Input;
using Avalonia.Interactivity;
using Nodify.Compatibility;

namespace JMCOM.Components.EpsonRobot.NodeFlow.Command;

public interface INodifyCommand : ICommand
{
    void RaiseCanExecuteChanged();
}

public class DelegateCommand : INodifyCommand
{
    private readonly Action _action;
    private readonly Func<bool>? _condition;

    public event EventHandler? CanExecuteChanged;

    public DelegateCommand(Action action, Func<bool>? executeCondition = default)
    {
        _action = action ?? throw new ArgumentNullException(nameof(action));
        _condition = executeCondition;
    }

    public bool CanExecute(object? parameter)
        => _condition?.Invoke() ?? true;

    public void Execute(object? parameter)
        => _action();

    public void RaiseCanExecuteChanged()
        => CanExecuteChanged?.Invoke(this, new EventArgs());
}

public class DelegateCommand<T> : INodifyCommand
{
    private readonly Action<T> _action;
    private readonly Func<T, bool>? _condition;

    public event EventHandler? CanExecuteChanged;

    public DelegateCommand(Action<T> action, Func<T, bool>? executeCondition = default)
    {
        _action = action ?? throw new ArgumentNullException(nameof(action));
        _condition = executeCondition;
    }

    public bool CanExecute(object? parameter)
    {
        if (parameter is T value)
        {
            return _condition?.Invoke(value) ?? true;
        }

        return _condition?.Invoke(default!) ?? true;
    }

    public void Execute(object? parameter)
    {
        if (parameter is T value)
        {
            _action(value);
        }
        else
        {
            _action(default!);
        }
    }

    public void RaiseCanExecuteChanged()
        => CanExecuteChanged?.Invoke(this, new EventArgs());
}

public class RequeryCommand : INodifyCommand
{
    private readonly Action _action;
    private readonly Func<bool>? _condition;

    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public RequeryCommand(Action action, Func<bool>? executeCondition = default)
    {
        _action = action ?? throw new ArgumentNullException(nameof(action));
        _condition = executeCondition;
    }

    public bool CanExecute(object? parameter)
        => _condition?.Invoke() ?? true;

    public void Execute(object? parameter)
        => _action();

    public void RaiseCanExecuteChanged() { }
}

public class RequeryCommand<T> : INodifyCommand
{
    private readonly Action<T> _action;
    private readonly Func<T, bool>? _condition;

    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public RequeryCommand(Action<T> action, Func<T, bool>? executeCondition = default)
    {
        _action = action ?? throw new ArgumentNullException(nameof(action));
        _condition = executeCondition;
    }

    public bool CanExecute(object? parameter)
    {
        if (parameter is T value)
        {
            return _condition?.Invoke(value) ?? true;
        }

        return _condition?.Invoke(default!) ?? true;
    }

    public void Execute(object? parameter)
    {
        if (parameter is T value)
        {
            _action(value);
        }
        else
        {
            _action(default!);
        }
    }

    public void RaiseCanExecuteChanged() { }
}

public static class CommandManager
{
    public static Dictionary<Type, List<CommandBinding>> staticCommandBindings = new Dictionary<Type, List<CommandBinding>>();
        
    public static void RegisterClassCommandBinding(Type type, CommandBinding cmd)
    {
        if (!staticCommandBindings.ContainsKey(type))
        {
            staticCommandBindings.Add(type, new List<CommandBinding>());
        }
        staticCommandBindings[type].Add(cmd);
    }

    public static event EventHandler? RequerySuggested;
        
    public static void InvalidateRequerySuggested()
    {
        RequerySuggested?.Invoke(null, EventArgs.Empty);
    }

    static CommandManager()
    {
        InputElement.GotFocusEvent.AddClassHandler<Interactive>(GotFocusEventHandler);
        InputElement.LostFocusEvent.AddClassHandler<Interactive>(LostFocusEventHandler);
        InputElement.PointerPressedEvent.AddClassHandler<Interactive>(PointerPressedEventHandler);
        InputElement.PointerReleasedEvent.AddClassHandler<Interactive>(PointerReleasedEventHandler);
        InputElement.KeyUpEvent.AddClassHandler<Interactive>(KeyUpEventHandler);
        InputElement.KeyDownEvent.AddClassHandler<Interactive>(KeyDownEventHandler);
    }

    private static void KeyDownEventHandler(Interactive sender, KeyEventArgs e)
    {
        InvalidateRequerySuggested();
    }

    private static void KeyUpEventHandler(Interactive sender, KeyEventArgs e)
    {
        InvalidateRequerySuggested();
    }

    private static void PointerReleasedEventHandler(Interactive sender, PointerReleasedEventArgs e)
    {
        InvalidateRequerySuggested();
    }

    private static void PointerPressedEventHandler(Interactive sender, PointerPressedEventArgs e)
    {
        InvalidateRequerySuggested();
    }

    private static void GotFocusEventHandler(Interactive sender, GotFocusEventArgs e)
    {
        InvalidateRequerySuggested();
    }

    private static void LostFocusEventHandler(Interactive sender, RoutedEventArgs e)
    {
        InvalidateRequerySuggested();
    }
}

//namespace Nodify.Compatibility;
public class CommandBinding
{
    public CommandBinding(RoutedCommand command,
        EventHandler<ExecutedRoutedEventArgs> executed = null,
        EventHandler<CanExecuteRoutedEventArgs> canExecute = null)
    {
        Command = command;
        if (executed != null) Executed += executed;
        if (canExecute != null) CanExecute += canExecute;
    }

    public RoutedCommand Command { get; }

    public event EventHandler<CanExecuteRoutedEventArgs> CanExecute;

    public event EventHandler<ExecutedRoutedEventArgs> Executed;

    internal bool DoCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        if (e.Handled) return true;

        var canExecute = CanExecute;
        if (canExecute == null)
        {
            if (Executed != null)
            {
                e.Handled = true;
                e.CanExecute = true;
            }
        }
        else
        {
            canExecute(sender, e);

            if (e.CanExecute)
            {
                e.Handled = true;
            }
        }

        return e.CanExecute;
    }

    internal bool DoExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        if (!e.Handled)
        {
            var executed = Executed;

            if (executed != null)
            {
                if (DoCanExecute(sender, new CanExecuteRoutedEventArgs(e.Command, e.Parameter)))
                {
                    executed(sender, e);
                    e.Handled = true;
                    return true;
                }
            }
        }

        return false;
    }
}